/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.epidemy

import org.apache.commons.codec.digest.DigestUtils
import scala.actors._
import scala.actors.remote._
import javax.jmdns._
import scala.collection.JavaConversions._
import org.apache.commons.codec.binary.{Hex, Base64}

import java.net.URL
import java.io.File
 
case class CacheInfo(cachedURL: URL, originalURL: URL, signature: String)
  
class ProgramBuilder(name: Symbol, clientSignature: String) {//}, options: SpreadOptions) {
  def file(root: File, path: Seq[String]): File = path match {
    case Nil => root
    case n :: rest => file(new File(root, n), rest)
  }
      
  def tempProgFile(programName: Symbol, path: Seq[String]) = {
    val f = file(new File(System.getProperty("user.home")), List(".epidemy", "temp", programName.toString) ++ path)
    f.getParentFile.mkdirs
    f
  }
  def tempProgServedFile(programName: Symbol, path: Seq[String]) = tempProgFile(programName, List("servedFiles") ++ path)
  
  import java.io._
  def copy(in: InputStream, out: OutputStream, close: Boolean = true) {
    val buf = new Array[Byte](1024)
    var len = 0
    while ({ len = in.read(buf); len > 0 })
      out.write(buf, 0, len)
    
    if (close) {
      in.close
      out.close
    }
  }
  def cache(programName: Symbol, url: URL): Option[CacheInfo] = {
    val urlFile: File = new File(url.getFile)
    lazy val cache = tempProgServedFile(programName, List(urlFile.getName))
    var cachedURL: URL = null
    if (url.getProtocol.equals("file")) {
      if (urlFile.isDirectory) {
        import java.util.zip._
        val out = new ZipOutputStream(new FileOutputStream(cache))
        var files = List[(File, String)]()
        def visit(f: File, path: String): Unit = {
          val newPath = if (path == null) f.getName else path + "/" + f.getName
          if (!f.isHidden && !f.getName.startsWith(".")) {
            if (f.isDirectory) {
              f.listFiles.map(c => visit(c, newPath))
            } else {
              files = files ++ List((f, newPath))
            }
          }
        }
        visit(urlFile, null)
        if (files.isEmpty) {
          println("Url " + url + " needs not be cached")
          return None
        } else {
          println("Compressing " + files.size + " files to " + cache)
          files.foreach(_ match { case (f, path) => 
                val in = new FileInputStream(f)
                out.putNextEntry(new ZipEntry(path))
                copy(in, out, false)
                in.close
            })
          out.close
          println("Copied " + url + "'s " + files.size + " files to " + cache)
          cachedURL = cache.toURI.toURL
        }
      } else {
        println("No need to cache " + url)
        cachedURL = url
      }
    } else {
      copy(url.openStream, new FileOutputStream(cache))
      println("Copied " + url + " to " + cache)
      cachedURL = cache.toURI.toURL
    }
    Some(CacheInfo(cachedURL, url, hash(cachedURL.openStream)))
  }
  def hash(str: String) = new String(new Hex().encode(DigestUtils.sha(str)))
  def hash(in: InputStream, close: Boolean = true) = {
    val f = new String(new Hex().encode(DigestUtils.sha(in)))
    if (close)
      in.close
    f
  }
  
  def classPath = {
    import java.io._
    import java.net._
    System.getProperty("java.class.path").split(File.pathSeparator).map(p => 
      try { new URL(p) } catch { case _ => new File(p).getCanonicalFile.toURI.toURL }
    )
  }
  
  lazy val cacheInfos = classPath.map(u => cache('Program, u)).filter(_ != None).map(_.get)
  lazy val signature = {
    val concat = clientSignature + cacheInfos.map(_.signature).foldLeft("") { _ + _ }
    hash(concat)
  }
  val modificationTime = System.currentTimeMillis
  //lazy val program = new Program(name, signature, modificationTime, options.stayAlive)
}